﻿/*
 * Copyright 2012 Rene Prost, Registrite ja Infosüsteemide Keskus 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

using System;
using System.CodeDom;
using System.Collections.Generic;
using Xtee.CodeDom.CodeBuilders;
using Xtee.CodeDom.MetaInfo;
using Xtee.Core.Exceptions;

namespace Xtee.CodeDom.CodeStatements
{
    internal class ArrayCodeStatements : CodeStatementsBase
    {
        public ArrayCodeStatements(InfoBase classPropertyInfo)
            : base(classPropertyInfo)
        {
        }

        protected override bool IsArray
        {
            get
            {
                
                return false;
            }
        }


        private List<CodeStatement> GetArrayItemTypeWriteStatements()
        {
            var statements = new List<CodeStatement>();

            TypeInfo ti = ElementPropertyInfo.ArrayItemElementInfo.TypeInfo;
            CodeStatementsBase generator = null;
            if (ti.IsBuiltIn())
                generator = new BuiltinTypeCodeStatements(PropertyInfo);
            else if (!ti.IsBuiltIn() && ti.IsUserDefined)
                generator = new UserDefinedTypeCodeStatements(PropertyInfo);
            else if (!ti.IsUserDefined)
                generator = new ComplexTypeCodeStatements(PropertyInfo);

            if (generator == null)
                throw new NotSupportedException("Code generation for type :" + PropertyInfo.FullTypeName +
                                                " aint available!");
            statements.AddRange(generator.GetWriteStatements(true)); 
            return statements;
        }
        private List<CodeStatement> GetItemContentReadStatements(ParticleType particle)
        {
            var statements = new List<CodeStatement>();
            TypeInfo ti = ElementPropertyInfo.ArrayItemElementInfo.TypeInfo;
            CodeStatementsBase generator = null;
            if (ti.IsBuiltIn())
                generator = new BuiltinTypeCodeStatements(PropertyInfo);
            else if (!ti.IsBuiltIn() && ti.IsUserDefined)
                generator = new UserDefinedTypeCodeStatements(PropertyInfo);
            else if (!ti.IsUserDefined)
                generator = new ComplexTypeCodeStatements(PropertyInfo);

            if (generator == null)
                throw new NotSupportedException("Code generation for type :" + PropertyInfo.FullTypeName +
                                                " aint available!");

            statements.AddRange(generator.GetReadStatements(particle));
            return statements;
        }



        protected override List<CodeStatement> GetTypeReadStatements(ParticleType particleType)
        {
            var statements = new List<CodeStatement>();
            var assignStatements = new List<CodeStatement>();
            var content = GetItemContentReadStatements(particleType);
            
            if (ElementPropertyInfo.IsSpecialArray)
            {
                statements.Add(GetArrayInitialization());
                statements.AddRange(GetReaderLoop(content));
                return statements;
            } 
            assignStatements.Add(GetArrayInitialization());
            assignStatements.AddRange(GetEmptyCheckCondition(GetArrayReadCodeStatements(content).ToArray())); 
            statements.AddRange(GetElementReadConditionBlock(particleType, assignStatements,false).ToArray()); 
            return statements;
        }

        private List<CodeStatement> GetArrayReadCodeStatements(List<CodeStatement> loopStatements)
        {
            var statements = new List<CodeStatement>();

            var elementName = GetElementName();

            statements.Add(GetReadStartElement(elementName, GetNamespace(),false));
            statements.AddRange(GetReaderLoop(loopStatements).ToArray());
            statements.Add(GetReadEndElement());

            return statements;
        }
       
        public override List<CodeStatement> GetTypeContentWriteStatement()
        {
            var statements = new List<CodeStatement>(); 
            if (ElementPropertyInfo.OccurenceInfo.MinOccurs == 0 || ElementPropertyInfo.IsSpecialArray)
                statements.AddRange(GetArrayContentStatements());
            else
            {
                CodeConditionStatement condition = GetNillabilityCondition(GetArrayContentStatements().ToArray()
                                                                           );
                statements.Add(condition);
            } 
            return statements;
        }

        private List<CodeStatement> GetArrayContentStatements()
        {
           var statements = new List<CodeStatement>();
            //if (!special)
            //    statements.AddRange(GetWriteArrayAttributes());

            if (ElementPropertyInfo.ArrayItemElementInfo.OccurenceInfo.MinOccurs == 0)
            {
                var conditionStatement = new List<CodeStatement>();
                conditionStatement.Add(
                    new CodeConditionStatement(GetElementNotEqualsNull(true),
                                               GetArrayItemTypeWriteStatements().ToArray()));
                statements.AddRange(GetWriterLoop(conditionStatement));
                return statements;
            }
             if (ElementPropertyInfo.ArrayItemElementInfo.OccurenceInfo.MinOccurs == 1)
            {
                 statements.AddRange(GetWriterLoop(GetArrayItemTypeWriteStatements()));
                return statements;
               
            }
            throw new NotImplementedException("Option not implemented!");
        }
         

        public CodeStatement GetArrayInitialization()
        {
            string arrayName = new PropertyBuilder().GetProperyTypeName(PropertyInfo);


            var st = new CodeAssignStatement(new CodeSnippetExpression("clazz." + PropertyInfo.Name),
                                             new CodeObjectCreateExpression(arrayName));

            return st;
        }
        
        public List<CodeStatement> GetReaderLoop(List<CodeStatement> loopStatements)
        {
            var minoccurrence = ElementPropertyInfo.ArrayItemElementInfo.OccurenceInfo.MinOccurs;
            var coll = new List<CodeStatement>();  
            var op = new CodeBinaryOperatorExpression(
                new CodeBinaryOperatorExpression(
                    GetReaderProperty("NodeType"),
                    CodeBinaryOperatorType.IdentityInequality,
                    GetXmlNodeTypeProperty("EndElement")
                    ),
                CodeBinaryOperatorType.BooleanAnd,
                new CodeBinaryOperatorExpression(
                    GetReaderProperty("LocalName"), CodeBinaryOperatorType.ValueEquality,
                    new CodePrimitiveExpression(GetElementName( true))
                    )
                );

            var whileloop = new CodeIterationStatement();
            whileloop.InitStatement = new CodeExpressionStatement(new CodeSnippetExpression(""));
            whileloop.TestExpression = op;
            whileloop.IncrementStatement = new CodeExpressionStatement(new CodeSnippetExpression(""));
            whileloop.Statements.AddRange(loopStatements.ToArray());


            coll.Add(whileloop);
            if (minoccurrence > 0)
            {
                var arrCountLt1 = new CodeBinaryOperatorExpression(
                    new CodePropertyReferenceExpression(
                        new CodeVariableReferenceExpression("clazz." + PropertyInfo.Name), "Count")
                    , CodeBinaryOperatorType.LessThan, new CodePrimitiveExpression(minoccurrence));


                var cond = new CodeConditionStatement(arrCountLt1, new CodeThrowExceptionStatement(
                                                                       new CodeObjectCreateExpression(
                                                                           "global::" + typeof(MinOccurrenceException),
                                                                           new CodePrimitiveExpression(minoccurrence),
                                                                           new CodePrimitiveExpression(GetElementName( true)))));
                var r = cond.ConvertToString();
                coll.Add(cond);
            }
#if (SHOW)
{  
  var val = coll.ConvertToString();
}
#endif
            return coll;
        }

        public List<CodeStatement> GetWriterLoop(List<CodeStatement> loopstatements)
        {
            var minoccurence = ElementPropertyInfo.ArrayItemElementInfo.OccurenceInfo.MinOccurs;
            var coll = new List<CodeStatement>();

            var condElementCount = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("x"),
                                                                    CodeBinaryOperatorType.LessThan,
                                                                    new CodeVariableReferenceExpression(
                                                                        GetElementAccessExpression(IsArray) +
                                                                        ".Count"));
            
            var condElementNotNull =
                new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression(GetElementAccessExpression(IsArray)),
                                                 CodeBinaryOperatorType.IdentityInequality,
                                                 new CodePrimitiveExpression(null));

            var loopTrueCond = new CodeBinaryOperatorExpression(
                condElementNotNull, CodeBinaryOperatorType.BooleanAnd,
                condElementCount
                );


            // Creates a for loop that sets x to 0 and continues incrementing x by 1 each loop until x is not less than 10.
            var forLoop = new CodeIterationStatement(
                new CodeVariableDeclarationStatement(typeof(int), "x",
                                                     new CodePrimitiveExpression(0)),
                loopTrueCond,
                new CodeAssignStatement(new CodeVariableReferenceExpression("x"), new CodeBinaryOperatorExpression(
                                                                                      new CodeVariableReferenceExpression
                                                                                          ("x"),
                                                                                      CodeBinaryOperatorType.Add,
                                                                                      new CodePrimitiveExpression(1))),
                loopstatements.ToArray()
                );

            if (minoccurence > 0)
            {
                var arrnotnull = new CodeBinaryOperatorExpression(
                    new CodeVariableReferenceExpression("clazz." + PropertyInfo.Name),
                    CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));


                var arrCountLt1 = new CodeBinaryOperatorExpression(
                    new CodePropertyReferenceExpression(
                        new CodeVariableReferenceExpression("clazz." + PropertyInfo.Name), "Count")
                    , CodeBinaryOperatorType.LessThan, new CodePrimitiveExpression(minoccurence));

                var or =
                    new CodeBinaryOperatorExpression(arrnotnull, CodeBinaryOperatorType.BooleanOr, arrCountLt1);


                var cond = new CodeConditionStatement(or, new CodeThrowExceptionStatement(
                                                              new CodeObjectCreateExpression(
                                                                  "global::" + typeof(MinOccurrenceException),
                                                                  new CodePrimitiveExpression(minoccurence),
                                                                  new CodePrimitiveExpression(
                                                                      GetElementName(true)))));

                coll.Add(cond);
            }
            coll.Add(forLoop);
#if (SHOW)
{  
  var val = coll.ConvertToString();
}
#endif
            return coll;
        }
    }
}